package com.yuanxin.yuanxinwebview;

import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.ValueCallback;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.network.ForwardingCookieHandler;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class YuanXinCookieModule extends ReactContextBaseJavaModule {

    private ForwardingCookieHandler cookieHandler;

    private static final String COOKIE_HEADER = "Cookie";
    private static final String VERSION_ZERO_HEADER = "Set-cookie";

    private static final String EXPIRES_FIELD = "=; Expires=Thu, 01 Jan 1970 00:00:00 GMT";
    private static final String DOMAIN_FIELD = "; Domain=";
    private static final String PATH_FIELD = "; Path=";

    private static final String OPTIONS_NAME = "name";
    private static final String OPTIONS_VALUE = "value";
    private static final String OPTIONS_DOMAIN = "domain";
    private static final String OPTIONS_ORIGIN = "origin";
    private static final String OPTIONS_PATH = "path";
    private static final String OPTIONS_EXPIRATION = "expiration";
    private static final String OPTIONS_VERSION = "version";


    public YuanXinCookieModule(ReactApplicationContext context) {
        super(context);
        this.cookieHandler = new ForwardingCookieHandler(context);
    }

    public String getName() {
        return "YuanXinCookieManager";
    }

    @ReactMethod
    public void set(ReadableMap options, final Callback callback) {
        Log.i("YuanXinWebViewManager", "options:" + options.toString());
        String name = null;
        String value = null;
        String domain = null;
        String origin = null;
        String path = null;
        String expiration = null;
        String version = null;
        if (options.hasKey(OPTIONS_NAME)) {
            name = options.getString(OPTIONS_NAME);
        }
        if (options.hasKey(OPTIONS_VALUE)) {
            value = options.getString(OPTIONS_VALUE);
        }
        if (options.hasKey(OPTIONS_DOMAIN)) {
            domain = options.getString(OPTIONS_DOMAIN);
        }
        if (options.hasKey(OPTIONS_ORIGIN)) {
            origin = options.getString(OPTIONS_ORIGIN);
        }
        if (options.hasKey(OPTIONS_PATH)) {
            path = options.getString(OPTIONS_PATH);
        }
        if (options.hasKey(OPTIONS_EXPIRATION)) {
            expiration = options.getString(OPTIONS_EXPIRATION);
        }
        if (options.hasKey(OPTIONS_EXPIRATION)) {
            version = options.getString(OPTIONS_EXPIRATION);
        }
        String cookieValue = name + "=" + value + ";"
                + OPTIONS_PATH + "=" + path + ";"
                + "expires" + "=" + expiration + ";"//安卓上的使用expires，ios使用expiration
                + OPTIONS_DOMAIN + "=" + domain;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            CookieManager.getInstance().setCookie(origin, cookieValue, new ValueCallback<Boolean>() {
                @Override
                public void onReceiveValue(Boolean value) {
                    Log.i(getName(),"setCookie:"+value);
                    if (callback != null)
                        callback.invoke(value);
                }
            });
            CookieManager.getInstance().flush();
        } else {
            CookieManager.getInstance().setCookie(origin, cookieValue);
            CookieSyncManager.createInstance(getCurrentActivity()).sync();
            if (callback != null)
                callback.invoke(true);
        }
    }


    @ReactMethod
    public void setFromResponse(String url, String value, final Callback callback) throws URISyntaxException, IOException {
        Map headers = new HashMap<String, List<String>>();
        // Pretend this is a header
        headers.put("Set-cookie", Collections.singletonList(value));
        URI uri = new URI(url);
        this.cookieHandler.put(uri, headers);
        callback.invoke(null, null);
    }

    @ReactMethod
    public void get(String url, Callback callback) throws URISyntaxException, IOException {
        URI uri = new URI(url);

        Map<String, List<String>> cookieMap = this.cookieHandler.get(uri, new HashMap());
        // If only the variables were public
        List<String> cookieList = cookieMap.get("Cookie");
        WritableMap map = Arguments.createMap();
        if (cookieList != null) {
            String[] cookies = cookieList.get(0).split(";");
            for (int i = 0; i < cookies.length; i++) {
                String[] cookie = cookies[i].split("=", 2);
                if (cookie.length > 1) {
                    map.putString(cookie[0].trim(), cookie[1]);
                }
            }
        }

        callback.invoke(null, map);
    }

    @ReactMethod
    public void getAll(Callback callback) throws Exception {
        throw new Exception("Cannot get all cookies on android, try getCookieHeader(url)");
    }

    @ReactMethod
    public void clearAll(final Callback callback) {
        this.cookieHandler.clearCookies(new Callback() {
            public void invoke(Object... args) {
                callback.invoke(null, null);
            }
        });
    }

    @ReactMethod
    public void clearByName(String url, Callback callback) {
        List<String> cookieList = null;
        URI uri = null;
        try {
            cookieList = getCookie(url);
            uri = new URI(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String domainURI = uri.getScheme() + "://" + uri.getHost() + (uri.getPort() == -1 ? "" : ":" + uri.getPort());

        if (cookieList != null) {
            String[] cookies = cookieList.get(0).split(";");

            // Expires each cookie with every possible domain and path option
            for (String cookie : cookies) {
                String[] parts = cookie.split("=");
                String path = "";
                String[] subPaths = uri.getPath().split("/");
                String name = parts[0].trim();
                String base = name + EXPIRES_FIELD;

                try {
                    setCookie(domainURI, base);
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if (subPaths.length == 0) {
                    subPaths = new String[]{""};
                }

                for (String subPath : subPaths) {
                    path += "/" + subPath;

                    String[] domains = uri.getHost().split("\\.");
                    String domain = domains[domains.length - 1];

                    for (int i = domains.length - 1; i > 0; i--) {
                        domain = domains[i - 1] + "." + domain;
                        try {
                            setCookie(domainURI, base + DOMAIN_FIELD + "." + domain + PATH_FIELD + path);
                            setCookie(domainURI, base + DOMAIN_FIELD + "." + domain + PATH_FIELD + path);
                        } catch (URISyntaxException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    try {
                        setCookie(domainURI, base + DOMAIN_FIELD + domain + PATH_FIELD + path);
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    if (path.equals("/")) {
                        path = "";
                    }
                }
            }
        }
        callback.invoke((Object) null);
    }

    private
    @Nullable
    List<String> getCookie(String url) throws URISyntaxException, IOException {
        URI uri = new URI(url);
        Map<String, List<String>> cookieMap = cookieHandler.get(uri, new HashMap<String, List<String>>());
        return cookieMap.get(COOKIE_HEADER);
    }

    private void setCookie(String url, String value) throws URISyntaxException, IOException {
        URI uri = new URI(url);
        Map<String, List<String>> cookieMap = new HashMap<>();
        cookieMap.put(VERSION_ZERO_HEADER, Collections.singletonList(value));
        cookieHandler.put(uri, cookieMap);
    }

}
